package affd.logic;

import java.util.ArrayList;

/**
 * NameReplacementFI is the name replacer for Finnish documents. 
 * Replaces the content of the given tokens declining the replacement word to the right case.
 */
public class NameReplacementFI implements NameReplacement {
	
	public static final String MODIFICATION_CLASS = "name";
	public static final String MODIFICATION_CLASS_ADDRESS = "address";
	
	public static final String LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	public static final int NUMBER_OF_ALPHABETS = 26; // LETTERS.length(), last index == 25

	private static final String[] POSSIBLE_ADDRESSES = {"tie", "katu", "kuja", "polku", "kaari", "rinne", "kadu", "polu", "kaare", "rintee"};
	private static final String[] WORD_CASES = new String[] {"", "n", "n", "a", "na", "ksi", "ssa", "sta", "an", "lla", "lta",
		"lle", "tta", "ineen", "n"};
	private static final String[] WORD_CASES_PLURAL = new String[] {"t", "iden", "t", "ita", "ina", "iksi", "issa", "ista", "ihin", "illa", "ilta",
		"ille", "itta", "ineen", "in"};
	private static final String[] CASE_NAMES = new String[] {
		"NOM", "GEN", "ACC", "PAR", "ESS", "TRA", "INE", "ELA", "ILL", "ADE", "ABL", "ALL", "ABE", "CMT", "INS"		
	};
	
	private int namesReplaced;
	private int addressesReplaced;
	private int emailsReplaced;
	private String nameReplacementWord;
	private String addressReplacementWord;
	private String emailReplacementWord;
	
	private HfstStarter hfstStarter;
	
	public NameReplacementFI() {
		nameReplacementWord = "Henkil\u00F6";
		addressReplacementWord = "Paikka";
		emailReplacementWord = "S\u00E4hk\u00F6postiosoite";
		namesReplaced = 0;
		addressesReplaced = 0;
		emailsReplaced = 0;

	}
	
	public void setHfstStarter(HfstStarter h) {
		hfstStarter = h;
	}
	
	/**
	 * Set a new word with which the names are replaced.
	 * @param newWord, new word with which the name is replaced
	 */
	public void setReplacementWord(String newWord) {
		nameReplacementWord = newWord;	
	}

	/**
	 * Modifies the content of each token in the given ArrayList of tokens to contain the replacement
	 * word instead of the name it was containing in the first place.
	 * 
	 * @param nameToReplace, The basic form of the name which is to be replaced
	 * @param allEntries, the ArrayList which contains all the instances of the name
	 */
	public void replaceAll(String nameToReplace, ArrayList<Token> allEntries) {

		boolean anythingReplaced = false;
		
		String groupId = Token.generateGroupId();
		
		for (Token token : allEntries) {
			if (token.getContent().trim().indexOf(nameReplacementWord) != 0 && token.getTag() == null) {
	    		String suffix = "";

	    		if (namesReplaced >= NUMBER_OF_ALPHABETS) {
	    			suffix += (namesReplaced / NUMBER_OF_ALPHABETS);
	    		}	    		

	    		boolean declined = isDeclined(token.getContent().trim(), nameToReplace);
	    		
	    		if (declined) {
	    			suffix += findRightSuffix(token, nameToReplace);
	    		}
	    		
	    		String newContent = nameReplacementWord + " " + LETTERS.charAt(namesReplaced - (namesReplaced / NUMBER_OF_ALPHABETS * NUMBER_OF_ALPHABETS)) + suffix;
	    		anythingReplaced = true;
	    		token.modifyContent(newContent, MODIFICATION_CLASS, groupId);
			}
		}

		if (anythingReplaced) {
			namesReplaced++;			
		}
	}

	/**
	 * 
	 * @param token
	 * @return
	 */
	private String findRightSuffix(Token token, String basicForm) {
		String content = token.getContent();
		String wordCase = "";
		
		if (hfstStarter != null) {
			String caseAndNumber = hfstStarter.getCaseAndNumber(basicForm, content);
			
			if (caseAndNumber != null) {
				String[] candn = caseAndNumber.split("\\;");
				if (candn.length > 1 && candn[1].equals("PL")) { // is plural
					wordCase += ":" + getCase(candn[0], true);
				} else if (candn.length > 1 && candn[1].equals("SG")) { // is single
					wordCase += ":" + getCase(candn[0], false);
				}
			}
		}
		return wordCase;
	}

	/**
	 * 
	 * @param s
	 * @return
	 */
	private String getCase(String s, boolean isPlural) {
		for (int i = 0; i < CASE_NAMES.length; i++) {
			if (s.equals(CASE_NAMES[i])) {
				if (isPlural) {
					return WORD_CASES_PLURAL[i];					
				} else {
					return WORD_CASES[i];
				}
			}
		}
		return "";
	}
	
	/**
	 * 
	 * @param tokenName
	 * @param basicForm
	 * @return
	 */
	private boolean isDeclined(String tokenName, String basicForm) {
		return !tokenName.equalsIgnoreCase(basicForm);
	}

	@Override
	/**
	 * 
	 */
	public void replaceAsAddress(String address, ArrayList<Token> tokens) {
		for (Token token : tokens) {
			// Check first if there is a number after the street name
			ArrayList<Token> siblings = token.getParent().getTokens();
			int index = siblings.indexOf(token);

    		String l = LETTERS.charAt(addressesReplaced - (addressesReplaced / NUMBER_OF_ALPHABETS * NUMBER_OF_ALPHABETS)) + "";
			
			String replacement = addressReplacementWord + " " + l;
			if (namesReplaced >= NUMBER_OF_ALPHABETS) {
    			replacement += (addressesReplaced / NUMBER_OF_ALPHABETS);
			}
			
			// Create group id
			String groupId = Token.generateGroupId();

			token.modifyContent(replacement, MODIFICATION_CLASS_ADDRESS, groupId);
			
			/*
			 * Checks if there is a number after the street name
			 */
			boolean isAddress = true;
			
			if (index < token.getParent().getTokens().size() - 2 &&
        			siblings.get(index + 1).getType() == Token.Type.WHITE_SPACE &&
        			(siblings.get(index + 2).getType() == Token.Type.WORD ||
        			siblings.get(index + 2).getType() == Token.Type.UNKNOWN)) {
				
        		try {
            		Integer.parseInt(siblings.get(index+2).getContent().trim());
        		} catch (NumberFormatException e) {
        			isAddress = false;
        		}
        		
        		if (isAddress) {
            		siblings.get(index+1).modifyContent("", MODIFICATION_CLASS_ADDRESS, groupId);
        			siblings.get(index+2).modifyContent("", MODIFICATION_CLASS_ADDRESS, groupId);
        			
        			index += 2;
    			
        			/*
        			 * Checks if there is a letter after the number
        			 */
        				if (index < token.getParent().getTokens().size() - 2 &&
    	        			siblings.get(index + 1).getType() == Token.Type.WHITE_SPACE &&
    	        			(siblings.get(index + 2).getType() == Token.Type.WORD ||
    	        			siblings.get(index + 2).getType() == Token.Type.UNKNOWN)) {
    										
    					if (siblings.get(index + 2).getOriginal().trim().length() == 1 && 
								LETTERS.contains(siblings.get(index + 2).getOriginal())) {
    						
    		        		siblings.get(index+1).modifyContent("", MODIFICATION_CLASS_ADDRESS, groupId);
    		    			siblings.get(index+2).modifyContent("", MODIFICATION_CLASS_ADDRESS, groupId);
    						
    						index += 2;

    						/*
    						 * Checks if there is a number after the letter which is after the first number
    						 */
    						if (index < token.getParent().getTokens().size() - 2 &&
    			        			siblings.get(index + 1).getType() == Token.Type.WHITE_SPACE &&
    			        			(siblings.get(index + 2).getType() == Token.Type.WORD ||
    			        			siblings.get(index + 2).getType() == Token.Type.UNKNOWN)) {
    							
    			        		try {
    			            		Integer.parseInt(siblings.get(index + 2).getContent().trim());	
    			        		} catch (NumberFormatException e) {
    			        			isAddress = false;
    			        		}
    			        		
    			        		if (isAddress) {
    				        		siblings.get(index+1).modifyContent("", MODIFICATION_CLASS_ADDRESS, groupId);
    				    			siblings.get(index+2).modifyContent("", MODIFICATION_CLASS_ADDRESS, groupId);	
    			        		}			        		
    						}
    					}
    				}
				}	
			}
		}
		addressesReplaced++;	
	}
	
	/**
	 * 
	 * @param word
	 * @return
	 */
	public boolean isAddress(String word) {
		
		for (String address : POSSIBLE_ADDRESSES) {
			if (word.contains(address)) {
				if (word.indexOf(address) > (word.length() - address.length() - 4)) { // 4 is the longest suffix
					return true;
				}
			}
		}
		return false;
	}
	
	public String getEmailReplacement() {
		String suffix = LETTERS.charAt(emailsReplaced - (emailsReplaced / NUMBER_OF_ALPHABETS * NUMBER_OF_ALPHABETS)) + "";
		
		String replacement = emailReplacementWord + " " + suffix;
		if (emailsReplaced >= NUMBER_OF_ALPHABETS) {
			replacement += (emailsReplaced / NUMBER_OF_ALPHABETS);
		}
		
		emailsReplaced++;
		
		return replacement;
	}
}
